#pragma once

#include <iostream>
#include <unistd.h>
#include "log.hpp"
#include <sys/types.h>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "threadpool.hpp"
#include "task.hpp"
#include "daemon.hpp"

const std::string defaultip = "0.0.0.0";
const int defaultport = 8888;
const int backlog = 5;

extern Log lg;

enum
{
    USAGE_ERR = 1,
    SOCK_ERR,
    BIND_ERR,
    LISTEN_ERR,
    ACCEPT_ERR
};

class tcpserver;

class pthread_data
{
public:
    pthread_data(int sockfd, const std::string &ip, const uint16_t &port, tcpserver *t)
        : sockfd_(sockfd), clientip(ip), clientport(port), tsvr(t)
    {
    }

public:
    int sockfd_;
    std::string clientip;
    uint16_t clientport;
    tcpserver *tsvr;
};

class tcpserver
{
public:
    tcpserver(const int &port = defaultport, const std::string &ip = defaultip)
        : listensock_(-1), ip_(ip), port_(port), isrunning_(false)
    {
    }

    void Init()
    {
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            lg(Fatal, "socket create error, sockfd: %d", listensock_);
            exit(SOCK_ERR);
        }
        lg(Info, "socket create success, sockfd: %d", listensock_);

        int opt = 1;
        // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &local.sin_addr);

        int n = bind(listensock_, (struct sockaddr *)&(local), sizeof(local));
        if (n < 0)
        {
            lg(Fatal, "bind error,errno: %d,errstring:%s ", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind  success!!!");

        n = listen(listensock_, backlog);
        if (n < 0)
        {
            lg(Fatal, "listen error,errno: %d,errstring:%s ", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        lg(Info, "listen  success!!!");
    }

    void Run()
    {
        lg.enable(ClassFile);
        Daemon();
        // signal(SIGPIPE,SIG_IGN);
        // 单例初始化并且创建线程池
        ThreadPool<task>::getInstance()->strart();

        // 如果在最前面调用：
        // for(;;) fork();
        // 这样也可以，但是很不美观，而且在竞争accept时需要加锁，避免同时调用。

        // 如果忽略子进程退出信号，就不用做等待处理了
        // signal(SIGCHLD,SIG_IGN);
        isrunning_ = true;
        lg(Info, "service  is running");
        while (true)
        {
            struct sockaddr_in client;
            memset(&client, 0, sizeof(client));
            socklen_t len = sizeof(client);
            // 1.listen套接字获取连接，连接成功就创建一个新的套接字来服务
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                lg(Warning, "accept error!!!");
                continue;
            }
            char clientip[16];
            inet_ntop(AF_INET, &client.sin_addr, clientip, sizeof(clientip));
            uint16_t clientport = ntohs(client.sin_port);
            lg(Info, "get a new link,accept success, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip, clientport);

            // 2.获取连接成功后，开始服务通信:

            // version1  -单进程版本，只能服务一个人
            // Service1(sockfd, clientip, clientport);
            // close(sockfd);

            // version2 -多进程版本
            // pid_t pid = fork();
            // if(pid == 0)
            // {
            //     //子进程
            //     close(listensock_);
            //     //子进程创建一个孙子进程，孙子帮忙完成服务，并且托孤给system，
            //     //就不用父进程等待了，父进程管好子进程就行了
            //     if(fork() > 0) exit(0);
            //     //孙子进程
            //     Service1(sockfd,clientip,clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // close(sockfd);
            // pid_t rid = waitpid(pid,nullptr,0);
            // (void)rid;

            // version3 -多线程版本
            // pthread_t tid;
            // pthread_data* td = new pthread_data(sockfd,clientip,clientport,this);
            // pthread_create(&tid,nullptr,RouTine,td);
            // 线程自己分离,因此不用join
            // pthread_join(tid,nullptr);

            // version4 -线程池版本(主要是为了控制线程的数量，只提供一次服务，而不是一直服务)
            task t(sockfd, clientip, clientport);
            ThreadPool<task>::getInstance()->push(t);
        }
    }

    // static void* RouTine(void* args)
    // {
    //     pthread_detach(pthread_self());
    //     pthread_data* td = static_cast<pthread_data*> (args);
    //     td->tsvr->Service1(td->sockfd_,td->clientip,td->clientport);

    //     close(td->sockfd_);
    //     delete td;
    //     return nullptr;
    // }

    // void Service1(int sockfd, const std::string &clientip, const uint16_t &clientport)
    // {
    //     char buffer[SIZE];
    //     while (true)
    //     {
    //         ssize_t n = read(sockfd, buffer, SIZE - 1);
    //         if (n > 0)
    //         {
    //             buffer[n] = 0;
    //             std::cout << "client say# " << buffer << std::endl;
    //             std::string echo_string = "serverce say# ";
    //             echo_string += buffer;
    //             n = write(sockfd, echo_string.c_str(), sizeof(echo_string));
    //         }
    //         else if (n == 0)
    //         {
    //             lg(Warning,"%s:%d quit, service sockfd: %d", clientip.c_str(), clientport,sockfd);
    //             break;
    //         }
    //         else
    //         {
    //             lg(Warning, "read error, sockfd: %d", sockfd);
    //             break;
    //         }
    //     }
    // }

    ~tcpserver()
    {
        if (listensock_ > 0)
        {
            close(listensock_);
        }
    }

private:
    int listensock_;
    std::string ip_;
    uint16_t port_;
    bool isrunning_;
};